﻿'    DotNetIRCD - An IRC Daemon written in VB.Net
'    Copyright (C) 2012 GeorgeBush
'
'    This program is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 2 of the License, or
'    (at your option) any later version.
'
'    This program is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.
'
'    You should have received a copy of the GNU General Public License
'    along with this program. If not, see <http://www.gnu.org/licenses/>.

''' <summary>
''' DotNetIRCD Logging
''' </summary>
''' <remarks>Provides logging functionality for DotNetIRCD</remarks>
Public Class Logging

    ''' <summary>
    ''' Storage for the current ConsoleColor settings
    ''' </summary>
    Private Shared pConsoleColors As New sConsoleColors(ConsoleColor.Red, ConsoleColor.DarkRed, ConsoleColor.Blue, ConsoleColor.White, ConsoleColor.Gray, ConsoleColor.Yellow)

    ''' <summary>
    ''' Storage for the current filename of the log file
    ''' </summary>
    Private Shared pLogFileName As String = "Log.txt"

    ''' <summary>
    ''' This object is used to avoid multiple threads from attempting file writes
    ''' at the same time.
    ''' </summary>
    Private Shared pSyncObject As New Object

    ''' <summary>
    ''' Get's or Set's the ConsoleColor values for the possible Log Types
    ''' </summary>
    ''' <value>sConsoleColors Structure</value>
    ''' <returns>sConsoleColors Structure</returns>
    Friend Shared Property ConsoleColors As sConsoleColors
        Get
            Return pConsoleColors
        End Get
        Set(ByVal value As sConsoleColors)
            pConsoleColors = value
        End Set
    End Property

    ''' <summary>
    ''' Adds a Message of the specified LogType to the specified LogTarget
    ''' </summary>
    ''' <param name="MessageType">The Type of the log message</param>
    ''' <param name="Target">The Target of the log message</param>
    ''' <param name="Message">The log message text</param>
    Public Shared Sub Log(ByVal MessageType As LogTypeEnum, ByVal Target As LogTargetEnum, ByVal Message As String)

        ' This makes it so that only one log write may happen at one time
        SyncLock pSyncObject

            ' Handle the Console target
            If Target And LogTargetEnum.Console Then

                ' Set new console color
                Select Case MessageType
                    Case LogTypeEnum.Fatal
                        Console.ForegroundColor = pConsoleColors.Fatal
                    Case LogTypeEnum.Error
                        Console.ForegroundColor = pConsoleColors.Error
                    Case LogTypeEnum.Warn
                        Console.ForegroundColor = pConsoleColors.Warn
                    Case LogTypeEnum.Info
                        Console.ForegroundColor = pConsoleColors.Info
                    Case LogTypeEnum.Debug
                        Console.ForegroundColor = pConsoleColors.Debug
                End Select

                ' Display the message
                If String.IsNullOrWhiteSpace(Message) Then
                    ' If Message was empty, write a blank line
                    Console.WriteLine("")
                Else
                    Console.WriteLine(String.Format("[{0:d/M/yyyy HH:mm:ss}][{1}] {2}", DateTime.Now, MessageType.ToString, Message))
                End If

            End If

            ' Handle the File target
            If Target And LogTargetEnum.File Then

                Dim objWriter As System.IO.StreamWriter = Nothing

                ' Check if the file specified in pLogFileName exists
                If Not My.Computer.FileSystem.FileExists(pLogFileName) Then

                    ' It does not exists, so let's create it
                    Try
                        objWriter = New System.IO.StreamWriter(pLogFileName, False)
                    Catch ex As Exception

                        ' Show an error if we could not access the file
                        Log(LogTypeEnum.Error, LogTargetEnum.Console, String.Format("Could not write to the Log file '{0}'", pLogFileName))
                        Log(LogTypeEnum.Error, LogTargetEnum.Console, ex.Message)

                    End Try

                Else

                    ' It exists so open for append
                    Try
                        objWriter = New System.IO.StreamWriter(pLogFileName, True)
                    Catch ex As Exception

                        ' Show an error if we could not access the file
                        Log(LogTypeEnum.Error, LogTargetEnum.Console, String.Format("Could not write to the Log file '{0}'", pLogFileName))
                        Log(LogTypeEnum.Error, LogTargetEnum.Console, ex.Message)

                    End Try

                End If

                ' Write the log entry
                If String.IsNullOrWhiteSpace(Message) Then
                    ' If Message was empty, write a blank line
                    objWriter.WriteLine("")
                Else
                    Try

                        objWriter.WriteLine(String.Format("[{0:d/M/yyyy HH:mm:ss}][{1}] {2}", DateTime.Now, MessageType.ToString, Message))

                    Catch ex As Exception
                        Log(LogTypeEnum.Error, LogTargetEnum.Console, String.Format("Could not write to the Log file '{0}'", pLogFileName))
                        Log(LogTypeEnum.Error, LogTargetEnum.Console, ex.Message)
                    End Try
                End If

                ' Check there was not an error and the StreamWriter was actually created
                If Not objWriter Is Nothing Then

                    ' Close the file.
                    objWriter.Close()

                    ' Dispose and NULL the objWriter object
                    objWriter.Dispose()
                    objWriter = Nothing

                End If

            End If

            ' Handle the EventLog target
            If Target And LogTargetEnum.EventLog Then

                ' Create a new EventLog object
                Dim objEventLog As New System.Diagnostics.EventLog

                Try

                    ' This fixes issue #1173
                    ' http://dotnetircd.codeplex.com/workitem/1173
                    If My.User.IsAuthenticated() And My.User.IsInRole(ApplicationServices.BuiltInRole.Administrator) Then

                        'Register the App as an Event Source
                        If Not EventLog.SourceExists("DotNetIRCD", ".") Then
                            EventLog.CreateEventSource("DotNetIRCD", "Application")
                        End If

                        ' Set the EventLog Message Source
                        objEventLog.Source = "DotNetIRCD"

                        ' Write the entry

                        If MessageType = LogTypeEnum.Fatal Or MessageType = LogTypeEnum.Error Then
                            objEventLog.WriteEntry(Message, EventLogEntryType.Error)
                        ElseIf MessageType = LogTypeEnum.Warn Then
                            objEventLog.WriteEntry(Message, EventLogEntryType.Warning)
                        ElseIf MessageType = LogTypeEnum.Info Or MessageType = LogTypeEnum.Debug Or MessageType = LogTypeEnum.InfoExt Then
                            objEventLog.WriteEntry(Message, EventLogEntryType.Information)
                        End If

                    End If

                Catch ex As Exception

                    ' Show an error if we could not access the file
                    Log(LogTypeEnum.Error, LogTargetEnum.Console, "Could not write to the EventLog.")
                    Log(LogTypeEnum.Error, LogTargetEnum.Console, ex.Message)

                End Try

            End If

            ' Handle the Debug target
            If Target And LogTargetEnum.Debug Then

                ' Write the message to Debug
                Debug.WriteLine(String.Format("[{0:d/M/yyyy HH:mm:ss}][{1}] {2}", DateTime.Now, MessageType.ToString, Message))

            End If

            ' Handle the Trace target
            If Target And LogTargetEnum.Trace Then

                ' Write the message to Trace
                Trace.WriteLine(String.Format("[{0:d/M/yyyy HH:mm:ss}][{1}] {2}", DateTime.Now, MessageType.ToString, Message))

            End If

        End SyncLock

    End Sub

End Class

''' <summary>
''' LogType Enumeration
''' </summary>
''' <remarks>Represents the type of log message</remarks>
Public Enum LogTypeEnum As Integer

    ''' <summary>
    ''' A Fatal Error Message
    ''' </summary>
    Fatal = 1

    ''' <summary>
    ''' A Non-Fatal Error Message
    ''' </summary>
    [Error] = 2

    ''' <summary>
    ''' A  Warning Message
    ''' </summary>
    Warn = 3

    ''' <summary>
    ''' Information Message
    ''' </summary>
    Info = 4

    ''' <summary>
    ''' Extended Information Message (Verbose)
    ''' </summary>
    InfoExt = 5

    ''' <summary>
    ''' A Debugging Message
    ''' </summary>
    Debug = 6

End Enum

''' <summary>
''' LogTarget Enumeration
''' </summary>
''' <remarks>Specifies the Output Target of the Log Message</remarks>
Public Enum LogTargetEnum As Integer

    ''' <summary>
    ''' Sends the Log Message to the Console Window
    ''' </summary>
    Console = 1

    ''' <summary>
    ''' Sends the Log Message to a File
    ''' </summary>
    File = 2

    ''' <summary>
    ''' Sends the Log Message to the Event Log
    ''' </summary>
    EventLog = 4

    ''' <summary>
    ''' Sends the Log Message to Debug using Debug.WriteLine
    ''' </summary>
    Debug = 8

    ''' <summary>
    ''' Sends the Log Message to Trace using Trace.WriteLine
    ''' </summary>
    Trace = 16

End Enum

''' <summary>
''' LogColors Structure
''' </summary>
''' <remarks>Stores the current ConsoleColor setting for each type of Log Message</remarks>
Public Structure sConsoleColors

    ''' <summary>
    ''' The ConsoleColor to use for Fatal Error Messages
    ''' </summary>
    Public Fatal As ConsoleColor

    ''' <summary>
    ''' The ConsoleColor to use for Error Messages
    ''' </summary>
    Public [Error] As ConsoleColor

    ''' <summary>
    ''' The ConsoleColor to use for Warning Messages
    ''' </summary>
    Public Warn As ConsoleColor

    ''' <summary>
    ''' The ConsoleColor to use for Info Messages
    ''' </summary>
    Public Info As ConsoleColor

    ''' <summary>
    ''' The ConsoleColor to use for Extended (Verbose) Info Messages
    ''' </summary>
    Public InfoExt As ConsoleColor

    ''' <summary>
    ''' The ConsoleColor to use for Debug  Messages
    ''' </summary>
    Public Debug As ConsoleColor

    ''' <summary>
    ''' Initialises a new sConsolecolors Structure
    ''' </summary>
    ''' <param name="FatalColor">The ConsoleColor to use for Fatal Error Messages</param>
    ''' <param name="ErrorColor">The ConsoleColor to use for Error Messages</param>
    ''' <param name="WarnColor">The ConsoleColor to use for Warning Messages</param>
    ''' <param name="InfoColor">The ConsoleColor to use for Info Messages</param>
    ''' <param name="InfoExtColor">The ConsoleColor to use for Extended (Verbose) Info Messages</param>
    ''' <param name="DebugColor">The ConsoleColor to use for Debug  Messages</param>
    Sub New(ByVal FatalColor As ConsoleColor, ByVal ErrorColor As ConsoleColor, ByVal WarnColor As ConsoleColor, ByVal InfoColor As ConsoleColor, ByVal InfoExtColor As ConsoleColor, ByVal DebugColor As ConsoleColor)

        Me.Fatal = FatalColor
        Me.Error = ErrorColor
        Me.Warn = WarnColor
        Me.Info = InfoColor
        Me.InfoExt = InfoExtColor
        Me.Debug = DebugColor

    End Sub

End Structure